/**************************************************************************
 * Copyright (C) 2017-2017  Unisound
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 **************************************************************************
 *
 * Description : main.c
 * Author      : wufangfang@unisound.com
 * Date        : 2019.09.12
 *
 **************************************************************************/

#include "uni_iot.h"
#include "uni_log.h"
#include "uni_timer.h"
#include "uni_event_route.h"
#include "uni_event_list.h"
#include "uni_recog_service.h"
#include "uni_media_player.h"
#include "uni_session_manage.h"
#include "uni_wakeup_session.h"
#include "uni_setting_session.h"
#include "uni_watchdog_session.h"
#include "uni_hal_mailbox.h"
#include "uni_black_board.h"
#include "idle_detect.h"
#include "uni_version.h"
#include "uni_record_save.h"
#include "uni_config.h"
#include "uni_local_source.h"
#include "uni_pcm_default.h"
#include "uni_cust_config.h"
#include "user_config.h"
#include "uni_user_meeting.h"
#include "user_main.h"
#include "user_player.h"
#include "ftscu.h"

#define MAIN_TAG                      "main"
#define CONFIG_ADDRESS_OFFSET         (0x4000)

static int _session_init(void);
static int _session_final(void);

static uni_sem_t g_hifi_init_sem;
static int _mailbox_hifi_init_handler(char *args,
                                      mailbox_message *message) {
  uni_sem_post(g_hifi_init_sem);
  printf("HIFI init completed !\r\n");
  return 0;
}

static void _wait_hifi_init_completed(void) {
  uni_sem_init(&g_hifi_init_sem, 0);
  sys_hal_mailbox_register_callback(MAILBOX_CTR_HIFI_INITED,
                                    _mailbox_hifi_init_handler, NULL);
  /* start HIFI after mailbox init */
  uni_start_hifi();
  printf("Waiting for HIFI init...\r\n");
  uni_sem_wait(g_hifi_init_sem);
}

static void _system_startup_status(void) {
  wakeup_type type;
  type = uni_get_boot_status();
  switch (type) {
    case WAKEUP_BY_POWERON:
      LOGT(MAIN_TAG, "WAKEUP_BY_POWERON");
      BbWrite(BB_KEY_BOOT_MUTE, false);
      break;
    case WAKEUP_BY_WATCHDOG:
      LOGT(MAIN_TAG, "WAKEUP_BY_WATCHDOG");
      BbWrite(BB_KEY_BOOT_MUTE, true);
      break;
    case WAKEUP_BY_RTC:
      LOGT(MAIN_TAG, "WAKEUP_BY_RTC");
      BbWrite(BB_KEY_BOOT_MUTE, true);
      break;
    case WAKEUP_BY_RESET_KEY:
      LOGT(MAIN_TAG, "WAKEUP_BY_RESET_KEY");
      BbWrite(BB_KEY_BOOT_MUTE, false);
      break;
    case WAKEUP_BY_HVAD:
      LOGT(MAIN_TAG, "WAKEUP_BY_HVAD");
      BbWrite(BB_KEY_BOOT_MUTE, true);
      BbWrite(BB_KEY_IS_HVAD_WAKEUP, true);
      break;
    case WAKEUP_BY_UNKNOWN:
      LOGT(MAIN_TAG, "WAKEUP_BY_UNKNOWN");
      BbWrite(BB_KEY_BOOT_MUTE, true);
      break;
    default:
      LOGT(MAIN_TAG, "wakeup status error !!!!");
      BbWrite(BB_KEY_BOOT_MUTE, true);
      break;
  }
}

static int _init_log_print(void) {
  uni_s64 tmp = 0;
  if (0 != ConfigReadItemNumber("log.enable", &tmp)) {
    printf("Cannot found log.enable, disable log print\r\n");
    return 0;
  }
  if (tmp) {
    if (0 != LogInitialize()) {
      printf("LogInitialize failed !\r\n");
      return -1;
    }
    LogLevelSet(N_LOG_NONE);
    if (0 == ConfigReadItemNumber("log.set_level", &tmp)) {
      LogLevelSet((LogLevel)tmp);
    }
  }
  return 0;
}

static void _play_boot_tone(void) {
#if !(USER_RUN_DEMO && \
      USER_RUN_DEMO_SELECT == USER_DEMO_NOISE_CTRL && \
      NOISE_REDUCTION_MODE == 1)
  int mute = false;
  BbRead(BB_KEY_BOOT_MUTE, &mute);
  LOGD(MAIN_TAG, "mute = %d", mute);
  if (!mute) {
#ifdef DEFAULT_PCM_WELCOME
    user_player_reply_list_random(DEFAULT_PCM_WELCOME);
#else
    user_player_reply_list_random(NICE_TO_MEET_YOU);
#endif
  }
  return;
#endif
}

static void _system_start_process(void) {
  MediaPlayerparam param = {1};
  _wait_hifi_init_completed();
  if (0 != ConfigInitialize(CONFIG_ADDRESS_OFFSET)) {
    goto L_CONFIG_INIT_FAILED;
  }
  if (0 != _init_log_print()) {
    goto L_LOG_INIT_FAILED;
  }
  LOGT(MAIN_TAG, "System start woking !");
#if (!USER_RUN_DEMO || USER_RUN_DEMO_SELECT != USER_DEMO_NOISE_CTRL)
  if (E_OK != RecordSaveInit()) {
    goto L_RECORD_SAVE_INIT_FAILED;
  }
#endif
  if (0 != BbInit()) {
    LOGE(MAIN_TAG, "Black board init failed");
    goto L_CLACK_BOARD_INIT_FAILED;
  }
  /* set black board key by stratup status */
  _system_startup_status();
  if (E_OK != TimerInitialize()) {
    LOGE(MAIN_TAG, "timer init failed");
    goto L_TIMER_INIT_FAILED;
  }
  if (0 != IdleDetectServiceInit()) {
    LOGE(MAIN_TAG, "Idle detect service init failed");
    goto L_IDLE_SERVICE_INIT_FAILED;
  }
  if (0 != LocalSourceInit()) {
    LOGE(MAIN_TAG, "local source init failed");
    goto L_LOCAL_SOURCE_INIT_FAILED;
  }
  if (E_OK != MediaPlayerInit(&param)) {
    LOGE(MAIN_TAG, "media player init failed");
    goto L_MEDIA_PLAYER_INIT_FAILED;
  }
  if (E_OK != RecogInit()) {
    LOGE(MAIN_TAG, "recog init failed");
    goto L_RECOG_INIT_FAILED;
  }
  if (E_OK != SessionManageInit()) {
    LOGE(MAIN_TAG, "session manage init failed");
    goto L_SESSION_MANAGER_INIT_FAILED;
  }
  if (0 != _session_init()) {
    LOGE(MAIN_TAG, "session init failed");
    goto L_SESSIONS_INIT_FAILED;
  }
  LOGT(MAIN_TAG, "System init successfuly !");

  /* call user main function to run user code */
  if (0 != uni_user_meeting_init()) {
    LOGE(MAIN_TAG, "user meeting init failed");
    goto L_USER_MEETING_FAILED;
  }
  if (0 != user_main()) {
    LOGE(MAIN_TAG, "user man init failed");
    goto L_USER_MAIN_FAILED;
  }

  _play_boot_tone();

  return;

  user_exit();
L_USER_MAIN_FAILED:
  uni_user_meeting_final();
L_USER_MEETING_FAILED:
  _session_final();
L_SESSIONS_INIT_FAILED:
  SessionManageFinal();
L_SESSION_MANAGER_INIT_FAILED:
  RecogFinal();
L_RECOG_INIT_FAILED:
  MediaPlayerFinal();
L_MEDIA_PLAYER_INIT_FAILED:
  LocalSourceFinal();
L_LOCAL_SOURCE_INIT_FAILED:
  IdleDetectServiceFinal();
L_IDLE_SERVICE_INIT_FAILED:
  TimerFinalize();
L_TIMER_INIT_FAILED:
  BbFinal();
L_CLACK_BOARD_INIT_FAILED:
#if (!USER_RUN_DEMO || USER_RUN_DEMO_SELECT != USER_DEMO_NOISE_CTRL)
  RecordSaveFinal();
L_RECORD_SAVE_INIT_FAILED:
#endif
  LogFinalize();
L_LOG_INIT_FAILED:
  ConfigFinalize();
L_CONFIG_INIT_FAILED:
  printf("System init failed !\r\n");
  return ;
}

static int _session_init(void) {
  if (E_OK != WakeupSessionInit()) {
    goto L_SESSION_WAKEUP_FAILED;
  }
  if (E_OK != SettingSessionInit()) {
    goto L_SESSION_SETTING_FAILED;
  }
  if (E_OK != WatchDogSessionInit()) {
    goto L_SESSION_WATCHDOG_FAILED;
  }
  return 0;

  WatchDogSessionFinal();
L_SESSION_WATCHDOG_FAILED:
  SettingSessionFinal();
L_SESSION_SETTING_FAILED:
  WakeupSessionFinal();
L_SESSION_WAKEUP_FAILED:
  return -1;
}

static int _session_final(void) {
  SettingSessionFinal();
  WakeupSessionFinal();
  WatchDogSessionFinal();
  return 0;
}

int main(void) {
  if (0 != uni_system_init()) {
    goto L_SYSTEM_INIT_FAILED;
  }

  uni_print_version();

  _system_start_process();

L_SYSTEM_INIT_FAILED:
  return 0;
}

